using System;
using System.Collections.Generic;
using System.Text;

using Mud.Common.Communication;
using Mud.Common.Physical;

namespace Mud.Common.Messages
{

    #region Response messages

    /// <summary>
    /// Message sent as affirmative response to request or command message.
    /// </summary>
	[Message(MessageTypeIds.ResponseOk)]
    public sealed class MessageResponseOk : MessageEmpty
    {
        public MessageResponseOk() : base(MessageTypeIds.ResponseOk) { }

        public static MessageResponseOk Instance
        {
            get { return _instance;  }
        }
        private static MessageResponseOk _instance = new MessageResponseOk();


        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <param name="target">The target.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(MessageResponseOk src, IMessage target)
        {
            return src.MessageTypeId == target.MessageTypeId;
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <param name="target">The target.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(MessageResponseOk src, IMessage target)
        {
            return src.MessageTypeId != target.MessageTypeId;
        }

    }


    /// <summary>
    /// Message sent as response to request or command message which tells that the request
    /// or command could not be processed because of some error.
    /// </summary>
    [Message(MessageTypeIds.ResponseError)]
    public sealed class MessageResponseError : MessageReflection
    {
        /// <summary>
        /// Describes the reason.
        /// </summary>
        public string Description = string.Empty;

        /// <summary>
        /// Initializes a new instance of the <see cref="MessageResponseError"/> class.
        /// </summary>
        public MessageResponseError() : base(MessageTypeIds.ResponseError) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="MessageResponseError"/> class.
        /// </summary>
        /// <param name="description">The error description.</param>
        public MessageResponseError(string description)
            : base(MessageTypeIds.ResponseError)
        {
            Description = description;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MessageResponseError"/> class with
        /// the ability to specify the formated string (as String.Format).
        /// </summary>
        /// <param name="description">The error description (same as String.Format).</param>
        /// <param name="p">The parameters for formatting.</param>
        public MessageResponseError(string description, params object[] p)
            : base(MessageTypeIds.ResponseError)
        {
            Description = string.Format(description, p);
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <param name="target">The target.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(MessageResponseError src, IMessage target)
        {
            return src.MessageTypeId == target.MessageTypeId;
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="src">The SRC.</param>
        /// <param name="target">The target.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(MessageResponseError src, IMessage target)
        {
            return src.MessageTypeId != target.MessageTypeId;
        }
    }

    /// <summary>
    /// Message sent as response to CommandLoginMessage.
    /// </summary>
    [Message(MessageTypeIds.ResponseLoginOk)]
    public sealed class MessageResponseLoginOk : MessageReflection
    {
        /// <summary>
        /// Returns the ID which is used to identify the user session. 
        /// </summary>
        public int SessionId;

        public MessageResponseLoginOk() : base(MessageTypeIds.ResponseLoginOk) { }
    }


    /// <summary>
    /// Message with server information.
    /// </summary>
    [Message(MessageTypeIds.ResponseServerInfo)]
    public sealed class MessageResponseServerInfo : MessageReflection
    {
        /// <summary>
        /// Current server lag.
        /// </summary>
        public float ServerLag;

        /// <summary>
        /// How long did it take for the last server cycle to complete (in milliseconds).
        /// </summary>
        public int LastTickDuration;

        /// <summary>
        /// Number of clients connected.
        /// </summary>
        public int ClientCount;

        /// <summary>
        /// Number of remote clients connected.
        /// </summary>
        public int RemoteClientCount;

        /// <summary>
        /// Commands processed in the last cycle.
        /// </summary>
        public int CommandMessagesProcessedLastTick;

        /// <summary>
        /// Commands processed since the server start.
        /// </summary>
        public int CommandMessagesProcessedTotal;

        /// <summary>
        /// Events sent in the last cycle.
        /// </summary>
        public int EventMessagesProcessedLastTick;

        /// <summary>
        /// Events sent since the server start.
        /// </summary>
        public int EventMessagesProcessedTotal;

        /// <summary>
        /// Date and time when the server has started.
        /// </summary>
        public DateTime ServerStarted;

        /// <summary>
        /// Name of the server.
        /// </summary>
        public string ServerName;

        public MessageResponseServerInfo() : base(MessageTypeIds.ResponseServerInfo) { }

    }

    /// <summary>
    /// Message sent as response to request for player characters.
    /// </summary>
    [Message(MessageTypeIds.ResponseGetPlayerCharacters)]
    public sealed class MessageResponseGetPlayerCharacters : MessageEmpty
    {

        public List<MessageStructureCreatureInfo> Characters =
            new List<MessageStructureCreatureInfo>();

        protected override void OnSerialize(BytePackage package)
        {
            package.Enqueue(Characters.Count);
            foreach (MessageStructureCreatureInfo info in Characters)
                info.Serialize(package);
        }

        protected override void OnDeserialize(BytePackage package)
        {
            int characterCount = package.DequeueInt();
            for(int i = 0; i< characterCount; i++) {
                MessageStructureCreatureInfo info = new MessageStructureCreatureInfo();
                info.Deserialize(package);
                Characters.Add(info);
            }
        }

        public MessageResponseGetPlayerCharacters() : base(MessageTypeIds.ResponseGetPlayerCharacters) { }

    }


    /// <summary>
    /// Message sent as affirmative response to request or command message.
    /// </summary>
    [Message(MessageTypeIds.ResponsePlay)]
    public sealed class MessageResponsePlay : MessageReflection
    {

        public MessageStructureCreatureInfo PlayerCharacter = new MessageStructureCreatureInfo();

        public MessageResponsePlay() : base(MessageTypeIds.ResponsePlay) { }
    }

    #endregion

    #region Server commands

    /// <summary>
    /// Message sent when user is requesting to log into Mud.
    /// </summary>
	[Message(MessageTypeIds.CommandLogin)]
	public sealed class MessageCommandLogin : MessageReflection
    {

        /// <summary>
        /// Users name.
        /// </summary>
        public string UserName = string.Empty;

        /// <summary>
        /// Users password.
        /// </summary>
        public string UserPassword = string.Empty;

        /// <summary>
        /// If set to some value it means that the user has already
        /// connected once and received an Id. 
        /// </summary>
        public int SessionId;

        /// <summary>
        /// If true then the client is requesting a command connection. If false
        /// then the client is requesting an event connection.
        /// </summary>
        public bool CommandTransporter;

		public MessageCommandLogin() : base(MessageTypeIds.CommandLogin) { }
    }



    /// <summary>
    /// Message sent when user is requesting to log out of Mud.
    /// </summary>
	[Message(MessageTypeIds.CommandLogout)]
	public sealed class MessageCommandLogout : MessageReflection
    {
		public MessageCommandLogout() : base(MessageTypeIds.CommandLogout) { }
    }

	/// <summary>
	/// Message sent as affirmative response to request or command message.
	/// </summary>
	[Message(MessageTypeIds.CommandGetServerInfo)]
	public sealed class MessageCommandGetServerInfo : MessageEmpty
	{
		public MessageCommandGetServerInfo() : base(MessageTypeIds.CommandGetServerInfo) { }
	}

	/// <summary>
	/// Request for the server to broadcast the message to all clients.
	/// </summary>
	[Message(MessageTypeIds.CommandServerBroadcast)]
	public sealed class MessageCommandServerBroadcast : MessageReflection
	{
		/// <summary>
		/// Message to broadcast to all clients.
		/// </summary>
        public string Message = string.Empty;

		public MessageCommandServerBroadcast() : base(MessageTypeIds.CommandServerBroadcast) { }
	}

	/// <summary>
	/// Command which will stop the server.
	/// </summary>
	[Message(MessageTypeIds.CommandStopServer)]
	public sealed class MessageCommandStopServer : MessageEmpty
	{
		public MessageCommandStopServer() : base(MessageTypeIds.CommandStopServer) { }

	}

    /// <summary>
    /// Request for the server to send all player characters information.
    /// </summary>
    [Message(MessageTypeIds.CommandGetPlayerCharacters)]
    public sealed class MessageCommandGetPlayerCharacters : MessageEmpty
    {
        public MessageCommandGetPlayerCharacters() : base(MessageTypeIds.CommandGetPlayerCharacters) { }
    }

    /// <summary>
    /// Request for the server to play with specific character.
    /// </summary>
    [Message(MessageTypeIds.CommandPlay)]
    public sealed class MessageCommandPlay : MessageReflection
    {

        public string CharacterName = string.Empty;

        public MessageCommandPlay() : base(MessageTypeIds.CommandPlay) { }
    }


	#endregion

	#region Server event messages

	/// <summary>
	/// Message which is sent to all clients as a result of CommandServerBroadcastMessage
	/// </summary>
	[Message(MessageTypeIds.EventServerBroadcast)]
	public sealed class MessageEventServerBroadcast : MessageReflection
	{
		/// <summary>
		/// Senders user name.
		/// </summary>
        public string SenderName = string.Empty;

		/// <summary>
		/// Message beeing broadcasted.
		/// </summary>
        public string Message = string.Empty;

		public MessageEventServerBroadcast() : base(MessageTypeIds.EventServerBroadcast) { }
	}

	/// <summary>
	/// This message is sent to clients when the server is stoping.
	/// </summary>
	[Message(MessageTypeIds.EventServerStopping)]
	public sealed class MessageEventServerStopping : MessageEmpty
	{
		public MessageEventServerStopping() : base(MessageTypeIds.EventServerStopping) { }
	}

	/// <summary>
	/// This message is sent to all clients when a client has connected.
	/// </summary>
	[Message(MessageTypeIds.EventClientConnected)]
	public sealed class MessageEventClientConnected : MessageReflection
	{
		/// <summary>
		/// Id of the client that has connected.
		/// </summary>
		public int ClientObjectId;

		/// <summary>
		/// Human friendly name of the client.
		/// </summary>
        public string ClientName = string.Empty;

        public MessageEventClientConnected() : base(MessageTypeIds.EventClientConnected) { }
	}


    /// <summary>
    /// This message is sent to all clients when a client has been disconnected.
    /// </summary>
    [Message(MessageTypeIds.EventClientDisconnected)]
    public sealed class MessageEventClientDisconnected : MessageReflection
    {
        /// <summary>
        /// Id of the client that is beeing disconnected.
        /// </summary>
        public int ClientObjectId;

        /// <summary>
        /// Human friendly name of the client.
        /// </summary>
        public string ClientName = string.Empty;

        public MessageEventClientDisconnected() : base(MessageTypeIds.EventClientDisconnected) { }
    }

	#endregion

    #region Controler command messages


    /// <summary>
    /// Command the controled creature to move to specific position.
    /// </summary>
    [Message(MessageTypeIds.CommandCreatureMove)]
    public sealed class MessageCommandCreatureMove : MessageReflection
    {
        /// <summary>
        /// X coordinate of the new location.
        /// </summary>
        public int X;

        /// <summary>
        /// Y coordinate of the new location.
        /// </summary>
        public int Y;

        /// <summary>
        /// How to move.
        /// </summary>
        public MovementType MovementType;

        public MessageCommandCreatureMove() : base(MessageTypeIds.CommandCreatureMove) { }
    }

    /// <summary>
    /// Command the controled creature to say something.
    /// </summary>
    [Message(MessageTypeIds.CommandCreatureSay)]
    public sealed class MessageCommandCreatureSay : MessageReflection
    {
        /// <summary>
        /// Id of the target creature (can be 0 which means no target).
        /// </summary>
        public int TargetCreatureId;

        /// <summary>
        /// Type of communication.
        /// </summary>
        public CommunicationType SayType;

        /// <summary>
        /// What is beeing said.
        /// </summary>
        public string Message = string.Empty;


        public MessageCommandCreatureSay() : base(MessageTypeIds.CommandCreatureSay) { }

    }


    #endregion

    #region Controler events

    /// <summary>
    /// Event sent when an object moves in the game.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureStartedMoving)]
    public sealed class MessageEventCreatureStartedMoving : MessageReflection
    {
        /// <summary>
        /// Id of the creature that started moving
        /// </summary>
        public int CreatureId;

        // Target coordinates
        public int X;
        public int Y;

        /// <summary>
        /// How long does it take to move to that location.
        /// </summary>
        public int Duration;


        /// <summary>
        /// Type of movement.
        /// </summary>
        public MovementType MovementType;

        public MessageEventCreatureStartedMoving() : base(MessageTypeIds.EventCreatureStartedMoving) { }
    }

    /// <summary>
    /// Event sent when an object finishes moving in the game.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureEndedMoving)]
    public sealed class MessageEventCreatureEndedMoving : MessageReflection
    {
        /// <summary>
        /// Id of the creature that ended moving
        /// </summary>
        public int CreatureId;

        // New coordinates
        public int X;
        public int Y;

        public MessageEventCreatureEndedMoving() : base(MessageTypeIds.EventCreatureEndedMoving) { }
    }

    /// <summary>
    /// Event sent when an object says something.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureSay)]
    public sealed class MessageEventCreatureSay : MessageReflection
    {
        /// <summary>
        /// Id of the creature that is saying
        /// </summary>
        public int SourceCreatureId;

        /// <summary>
        /// Id of the target object (can be 0 which means no target).
        /// </summary>
        public int TargetObjectId;

        /// <summary>
        /// Type of communication.
        /// </summary>
        public CommunicationType SayType;

        /// <summary>
        /// What is beeing said.
        /// </summary>
        public string Message = string.Empty;

        public MessageEventCreatureSay() : base(MessageTypeIds.EventCreatureSay) { }
    }


    /// <summary>
    /// Event that is sent when a creature enters an area;
    /// </summary>
    [Message(MessageTypeIds.EventCreatureEnteredArea)]
    public sealed class MessageEventCreatureEnteredArea : MessageReflection
    {
        /// <summary>
        /// Which area.
        /// </summary>
        public string AreaName = string.Empty;

        /// <summary>
        /// Which creature.
        /// </summary>
        public MessageStructureCreatureInfo Creature = new MessageStructureCreatureInfo();

        public MessageEventCreatureEnteredArea() : base(MessageTypeIds.EventCreatureEnteredArea) { }
    }

    /// <summary>
    /// Event that is sent when an object exits an area;
    /// </summary>
    [Message(MessageTypeIds.EventCreatureExitedArea)]
    public sealed class MessageEventCreatureExitedArea : MessageReflection
    {
        /// <summary>
        /// Which area.
        /// </summary>
        public string AreaName = string.Empty;

        /// <summary>
        /// Which creature.
        /// </summary>
        public int CreatureId;

        public MessageEventCreatureExitedArea() : base(MessageTypeIds.EventCreatureExitedArea) { }
    }

    /// <summary>
    /// Event that is sent when a creature appears (becomes visible) to the player character.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureAppeared)]
    public sealed class MessageEventCreatureAppeared : MessageReflection
    {

        /// <summary>
        /// Which creature.
        /// </summary>
        public MessageStructureCreatureInfo Creature = new MessageStructureCreatureInfo();

        public MessageEventCreatureAppeared() : base(MessageTypeIds.EventCreatureAppeared) { }
    }


    /// <summary>
    /// Event that is sent when a creature disapears (is no longer visible) to the player character.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureDisappeared)]
    public sealed class MessageEventCreatureDisappeared : MessageReflection
    {
        /// <summary>
        /// Which creature.
        /// </summary>
        public int CreatureId;

        public MessageEventCreatureDisappeared() : base(MessageTypeIds.EventCreatureDisappeared) { }
    }

    /// <summary>
    /// Event that is sent when a creature turns to some direction.
    /// </summary>
    [Message(MessageTypeIds.EventCreatureTurned)]
    public sealed class MessageEventCreatureTurned : MessageReflection
    {
        /// <summary>
        /// Which creature.
        /// </summary>
        public int CreatureId;

        /// <summary>
        /// To which direction.
        /// </summary>
        public Orientation Direction;

        public MessageEventCreatureTurned() : base(MessageTypeIds.EventCreatureTurned) { }
    }

    #endregion

    #region Object events

    /// <summary>
    /// Event that is sent when an object appears (becomes visible) to the player character.
    /// </summary>
    [Message(MessageTypeIds.EventSimpleObjectAppeared)]
    public sealed class MessageEventSimpleObjectAppeared : MessageReflection
    {

        /// <summary>
        /// Which object.
        /// </summary>
        public MessageStructureSimpleObjectInfo SimpleObject = new MessageStructureSimpleObjectInfo();

        public MessageEventSimpleObjectAppeared() : base(MessageTypeIds.EventSimpleObjectAppeared) { }
    }


    /// <summary>
    /// Event that is sent when an object disapears (is no longer visible) to the player character.
    /// </summary>
    [Message(MessageTypeIds.EventSimpleObjectDisappeared)]
    public sealed class MessageEventSimpleObjectDisappeared : MessageReflection
    {
        /// <summary>
        /// Which object.
        /// </summary>
        public int SimpleObjectId;

        public MessageEventSimpleObjectDisappeared() : base(MessageTypeIds.EventSimpleObjectDisappeared) { }
    }

    #endregion

    #region Area events

    /// <summary>
    /// Event that is sent when players character area changes.
    /// </summary>
    [Message(MessageTypeIds.EventAreaChanged)]
    public sealed class MessageEventAreaChanged : MessageReflection
    {
        /// <summary>
        /// Which object.
        /// </summary>
        public MessageStructureAreaInfo Area = new MessageStructureAreaInfo();

        public MessageEventAreaChanged() : base(MessageTypeIds.EventAreaChanged) { }
    }

    /// <summary>
    /// Event that is sent when area land changes (made visible or changed in some other way).
    /// </summary>
    [Message(MessageTypeIds.EventAreaLandChanged)]
    public sealed class MessageEventAreaLandChanged : MessageReflection
    {
        /// <summary>
        /// Which object.
        /// </summary>
        public MessageStructureAreaLandInfo Land = new MessageStructureAreaLandInfo();

        public MessageEventAreaLandChanged() : base(MessageTypeIds.EventAreaLandChanged) { }
    }

    /// <summary>
    /// Event sent when someone says something on some location.
    /// </summary>
    [Message(MessageTypeIds.EventLocationSay)]
    public sealed class MessageEventLocationSay : MessageReflection
    {

        // origin of the voice
        public int X;
        public int Y;


        /// <summary>
        /// Type of communication.
        /// </summary>
        public CommunicationType SayType;

        /// <summary>
        /// What is beeing said.
        /// </summary>
        public string Message = string.Empty;

        public MessageEventLocationSay() : base(MessageTypeIds.EventLocationSay) { }
    }

    #endregion

    #region Message parts

    /// <summary>
    /// Represents a structure which holds common information to both creature and simple object.
    /// </summary>
    [Message(MessageTypeIds.StructurePhysicalObjectInfo)]
    public sealed class MessageStructurePhysicalObjectInfo : MessageReflection
    {
        public int ObjectId;
        public string ObjectName = string.Empty;
        public string AreaName = string.Empty;
        public int LocationX;
        public int LocationY;
        public Orientation Orientation;

        public MessageStructurePhysicalObjectInfo() : base(MessageTypeIds.StructurePhysicalObjectInfo) { }
    }

    /// <summary>
    /// Represents a structure which holds information about a creature.
    /// </summary>
    [Message(MessageTypeIds.StructureCreatureInfo)]
    public sealed class MessageStructureCreatureInfo : MessageReflection
    {
        public MessageStructurePhysicalObjectInfo ObjectInfo = new MessageStructurePhysicalObjectInfo();

        public MessageStructureCreatureInfo() : base(MessageTypeIds.StructureCreatureInfo) { }
    }

    /// <summary>
    /// Represents a structure which holds information about a creature.
    /// </summary>
    [Message(MessageTypeIds.StructureSimpleObjectInfo)]
    public sealed class MessageStructureSimpleObjectInfo : MessageReflection
    {
        public MessageStructurePhysicalObjectInfo ObjectInfo = new MessageStructurePhysicalObjectInfo();

        public MessageStructureSimpleObjectInfo() : base(MessageTypeIds.StructureSimpleObjectInfo) { }
    }

    /// <summary>
    /// Represents a structure which holds information about an area.
    /// </summary>
    [Message(MessageTypeIds.StructureAreaInfo)]
    public sealed class MessageStructureAreaInfo : MessageReflection
    {
        public string AreaName = string.Empty;
        public int AreaXDimension;
        public int AreaYDimension;

        public MessageStructureAreaInfo() : base(MessageTypeIds.StructureAreaInfo) { }
    }

    /// <summary>
    /// Represents a structure which holds information about a part of an area.
    /// </summary>
    [Message(MessageTypeIds.StructureAreaLandInfo)]
    public sealed class MessageStructureAreaLandInfo : MessageEmpty
    {
        public int XOffset;

        public int YOffset;

        public int Width;

        public int Height;

        public TerrainType[,] Terrain;


        protected override void OnSerialize(BytePackage package)
        {
            if (Terrain == null || Terrain.GetUpperBound(0) != Width-1 || Terrain.GetUpperBound(1) != Height-1)
                throw new InvalidOperationException("Invalid number of terrain tiles.");

            package.Enqueue(XOffset);
            package.Enqueue(YOffset);
            package.Enqueue(Width);
            package.Enqueue(Height);
            for(int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                    package.Enqueue((byte)Terrain[x,y]);
        }

        protected override void OnDeserialize(BytePackage package)
        {
            XOffset = package.DequeueInt();
            YOffset = package.DequeueInt();
            Width = package.DequeueInt();
            Height = package.DequeueInt();
            Terrain = new TerrainType[Width, Height];

            for (int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                    Terrain[x, y] = (TerrainType)package.DequeueByte();
        }


        public MessageStructureAreaLandInfo() : base(MessageTypeIds.StructureAreaLandInfo) { }
    }

    #endregion

}
